#include "Frameless.h"

#include <QWidget>
#include <QMouseEvent>
#include <QLayout>
#include <QRect>
#include <QApplication>

#ifdef TDESKTOP_UOS_BUILD
#include "qplatformnativeinterface.h"
#endif

#define PADDING 3

bool canUseCompositing()
{
#ifdef TDESKTOP_UOS_BUILD
    QPlatformNativeInterface *native = qApp->platformNativeInterface();
    if (Q_LIKELY(native)) {
        QScreen *scr = QGuiApplication::primaryScreen();
        return native->nativeResourceForScreen(QByteArray("compositingEnabled"), scr);
    }
#endif
    return true;
}

Frameless::Frameless(QWidget *self)
    : m_self(self)
    , dir(Direction::NONE)
    , leftButtonPress(false)
{
    m_self->setWindowFlags(m_self->windowFlags() | Qt::FramelessWindowHint);
    m_self->setAttribute(Qt::WA_TranslucentBackground, canUseCompositing());
}

bool Frameless::canResize()
{
    return true;
}

void Frameless::focusInEvent(QFocusEvent *e)
{
    Q_UNUSED(e);
    if (!canResize())
        return;

    this->calcRegion(m_self->cursor().pos());
}

void Frameless::calcRegion(const QPoint &cursorGlobalPoint)
{
    QRect rect = m_self->rect() - layoutMargins() / 2;
    QPoint tl = m_self->mapToGlobal(rect.topLeft());
    QPoint rb = m_self->mapToGlobal(rect.bottomRight());

    int x = cursorGlobalPoint.x();
    int y = cursorGlobalPoint.y();
    Qt::CursorShape cursorShape = Qt::ArrowCursor;

    if (tl.x() + PADDING >= x
            && tl.x() <= x
            && tl.y() + PADDING >= y
            && tl.y() <= y) {
        dir = LEFTTOP;
        cursorShape = Qt::SizeFDiagCursor;
    } else if (x >= rb.x() - PADDING
              && x <= rb.x()
              && y >= rb.y() - PADDING
              && y <= rb.y()) {
        dir = RIGHTBOTTOM;
        cursorShape = Qt::SizeFDiagCursor;
    } else if (x <= tl.x() + PADDING
              && x >= tl.x()
              && y >= rb.y() - PADDING
              && y <= rb.y()) {
        dir = LEFTBOTTOM;
        cursorShape = Qt::SizeBDiagCursor;
    } else if (x <= rb.x()
              && x >= rb.x() - PADDING
              && y >= tl.y()
              && y <= tl.y() + PADDING) {
        dir = RIGHTTOP;
        cursorShape = Qt::SizeBDiagCursor;
    } else if (x <= tl.x() + PADDING && x >= tl.x()) {
        dir = LEFT;
        cursorShape = Qt::SizeHorCursor;
    } else if ( x <= rb.x() && x >= rb.x() - PADDING) {
        dir = RIGHT;
        cursorShape = Qt::SizeHorCursor;
    } else if (y >= tl.y() && y <= tl.y() + PADDING) {
        dir = UP;
        cursorShape = Qt::SizeVerCursor;
    } else if (y <= rb.y() && y >= rb.y() - PADDING) {
        dir = DOWN;
        cursorShape = Qt::SizeVerCursor;
    } else {
        dir = NONE;
        cursorShape = Qt::ArrowCursor;
    }

    m_self->setCursor(QCursor(cursorShape));
}

//void Frameless::resetResource()
//{
//    dir = NONE;
//    m_self->setCursor(QCursor(Qt::ArrowCursor));
//    leftButtonPress = false;
//}

void Frameless::event(QEvent *e)
{
    if (e->type() == QEvent::Create) {
        m_self->setMouseTracking(canResize());
    }
}

bool Frameless::mousePressEvent(QMouseEvent *event)
{
    bool ret = false;
    switch (event->button()) {
    case Qt::LeftButton: {
        m_point = event->pos();
        if (dir != NONE) {
            m_self->mouseGrabber();
        } else {
            if (canWindowMove()) {
                dragPosition = event->globalPos() - m_self->frameGeometry().topLeft();
                m_self->setCursor(QCursor(Qt::SizeAllCursor));
                this->leftButtonPress = true;
            }
        }

        ret = true;
        break;
    }
    default:
        break;
    }

    event->accept();
    return ret;
}

void Frameless::mouseMoveEvent(QMouseEvent*event)
{
    QPoint gloPoint = event->globalPos();
    QRect rect = m_self->rect();
    QPoint tl = m_self->mapToGlobal(rect.topLeft());
    QPoint rb = m_self->mapToGlobal(rect.bottomRight());

    if (!(event->buttons() & Qt::LeftButton)) {
        this->calcRegion(gloPoint);
    } else {
        if ((dir != NONE) && canResize()) {
            if (m_self->parent()) {
                gloPoint = m_self->mapToParent(m_self->mapFromGlobal(gloPoint));
                tl = m_self->mapToParent(m_self->mapFromGlobal(tl));
                rb = m_self->mapToParent(m_self->mapFromGlobal(rb));
            }

            QRect rMove(tl, rb);

            switch (dir) {
            case LEFT: {
                if (rb.x() - gloPoint.x() <= m_self->minimumWidth()) {
                    rMove.setX(tl.x());
                } else {
                    rMove.setX(gloPoint.x());
                }
            }
                break;
            case RIGHT: {
                if (gloPoint.x() - tl.x() <= m_self->minimumWidth()) {
                    rMove.setX(tl.x());
                } else {
                    rMove.setX(tl.x());
                    rMove.setWidth(gloPoint.x() - tl.x());
                }
            }
                break;
            case UP: {
                if (rb.y() - gloPoint.y() <= m_self->minimumHeight()) {
                    rMove.setY(tl.y());
                } else {
                    rMove.setY(gloPoint.y());

                }
            }
                break;
            case DOWN: {
                if (gloPoint.y() - tl.y() <= m_self->minimumHeight()) {
                    rMove.setY(tl.y());
                } else {
                    rMove.setY(tl.y());
                    rMove.setHeight(gloPoint.y() - tl.y());
                }
            }
                break;
            case LEFTTOP: {
                if (rb.x() - gloPoint.x() <= m_self->minimumWidth()) {
                    rMove.setX(tl.x());
                } else {
                    rMove.setX(gloPoint.x());
                }

                if (rb.y() - gloPoint.y() <= m_self->minimumHeight()) {
                    rMove.setY(tl.y());
                } else {
                    rMove.setY(gloPoint.y());
                }
            }
                break;
            case RIGHTTOP: {
                if (gloPoint.x() - tl.x() <= m_self->minimumWidth()) {
                    rMove.setX(tl.x());
                } else {
                    rMove.setX(tl.x());
                    rMove.setWidth(gloPoint.x() - tl.x());
                }

                if (rb.y() - gloPoint.y() <= m_self->minimumHeight()) {
                    rMove.setY(tl.y());
                } else {
                    rMove.setY(gloPoint.y());
                }
            }
                break;
            case LEFTBOTTOM: {
                if (rb.x() - gloPoint.x() <= m_self->minimumWidth()) {
                    rMove.setX(tl.x());
                } else {
                    rMove.setX(gloPoint.x());
                }

                if (gloPoint.y() - tl.y() <= m_self->minimumHeight()) {
                    rMove.setY(tl.y());
                } else {
                    rMove.setY(tl.y());
                    rMove.setHeight(gloPoint.y() - tl.y());
                }
            }
                break;
            case RIGHTBOTTOM: {
                if (gloPoint.x() - tl.x() <= m_self->minimumWidth()) {
                    rMove.setX(tl.x());
                } else {
                    rMove.setX(tl.x());
                    rMove.setWidth(gloPoint.x() - tl.x());
                }

                if (gloPoint.y() - tl.y() <= m_self->minimumHeight()) {
                    rMove.setY(tl.y());
                } else {
                    rMove.setY(tl.y());
                    rMove.setHeight(gloPoint.y() - tl.y());
                }
            }
                break;
            default:
                break;
            }

            m_self->setGeometry(rMove);
        } else {
            if (leftButtonPress) {
                if (m_self->isMaximized()) {
                    double xRatio = event->globalX() * 1.0 / m_self->width();
                    double yRatio = event->globalY() * 1.0 / m_self->height();
                    m_self->showNormal();
                    int widgetX = m_self->width() * xRatio;
                    int widgetY = m_self->height() * yRatio;
                    m_self->move(event->globalX() - widgetX, event->globalY() - widgetY);
                    m_self->layout()->setMargin(10);
                    dragPosition = event->globalPos() - m_self->frameGeometry().topLeft();
                    return;
                }

                m_self->move(event->globalPos() - dragPosition);
                event->accept();
            }
        }
    }
}

void Frameless::mouseReleaseEvent(QMouseEvent* event)
{
    Q_UNUSED(event);

    m_self->setCursor(QCursor(Qt::ArrowCursor));
    leftButtonPress = false;
}
